package com.un.ebs.finance.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.un.ebs.core.config.K3Config;
import com.un.ebs.core.exception.BusinessEntityNotFoundException;
import com.un.ebs.core.exception.BusinessException;
import com.un.ebs.core.util.DateUtils;
import com.un.ebs.core.util.SecurityUtils;
import com.un.ebs.core.util.StringUtils;
import com.un.ebs.finance.AccountTypeEnum;
import com.un.ebs.finance.WebService1;
import com.un.ebs.finance.WebService1Soap;
import com.un.ebs.finance.dto.CertificateModel;
import com.un.ebs.finance.dto.CustomerDto;
import com.un.ebs.finance.dto.FDetailDto;
import com.un.ebs.finance.dto.K3Result;
import com.un.ebs.finance.service.FinanceSyncService;
import com.un.ebs.inventory.InventoryConfigureCode;
import com.un.ebs.inventory.domain.AccountMonth;
import com.un.ebs.inventory.domain.EntRelationExt;
import com.un.ebs.inventory.enums.BusinessRelationTypeEnum;
import com.un.ebs.inventory.enums.InventoryTypeEnum;
import com.un.ebs.inventory.service.BusinessRelationService;
import com.un.ebs.inventory.service.InventoryNoteService;
import com.un.ebs.inventory.service.InventoryService;
import com.un.ebs.invoice.domain.InvoiceNoteByPurchase;
import com.un.ebs.invoice.domain.InvoiceNoteByPurchaseSum;
import com.un.ebs.invoice.domain.InvoiceNoteExt;
import com.un.ebs.invoice.enums.InvoiceTypeEnum;
import com.un.ebs.invoice.service.InvoiceNoteService;
import com.un.ebs.purchase.domain.PurchaseMonthSumExt;
import com.un.ebs.purchase.service.PurchaseOrderService;
import com.un.ebs.sale.domain.FinanceBySaleExt;
import com.un.ebs.sale.service.SaleOrderService;
import com.un.ebs.sys.service.ConfigureService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;

@Service
public class FinanceSyncServiceImpl implements FinanceSyncService {

    @Resource
    private BusinessRelationService businessRelationService;

    @Resource
    private PurchaseOrderService purchaseOrderService;

    @Resource
    private InventoryNoteService inventoryNoteService;

    @Resource
    private SaleOrderService saleOrderService;

    @Resource
    private InvoiceNoteService invoiceNoteService;

    @Resource
    private InventoryService inventoryService;

    @Resource
    private ConfigureService configureService;

    @Resource
    private K3Config k3Config;

    private WebService1Soap soap;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void syncBaseData(BusinessRelationTypeEnum type) {
        WebService1 ws = new WebService1();
        WebService1Soap soap = ws.getWebService1Soap();
        for (EntRelationExt item : businessRelationService.query(null, type, -1, 1).getRecords()) {
            String strRet;
            switch (type) {
                case CUSTOMER:
                    strRet = soap.qureyCustomer(item.getCode().toString(), k3Config.getUserName(), k3Config.getPassword());
                    break;
                case SUPPLIER:
                    strRet = soap.qureySupplier(item.getCode().toString(), k3Config.getUserName(), k3Config.getPassword());
                    break;
                case DEFAULT:
                default:
                    logger.debug("往来单位类别不正确");
                    return;
            }

            try {
                CustomerDto obj = JSON.parseObject(strRet).toJavaObject(CustomerDto.class);
                System.out.println(obj);
            } catch (JSONException e) {
                switch (type) {
                    case CUSTOMER:
                        strRet = soap.customer("0", item.getCode().toString(), item.getName(), k3Config.getUserName(), k3Config.getPassword());
                        break;
                    case SUPPLIER:
                        strRet = soap.supplier("0", item.getCode().toString(), item.getName(), k3Config.getUserName(), k3Config.getPassword());
                        break;
                    case DEFAULT:
                    default:
                        logger.debug("往来单位类别不正确");
                        return;
                }

                //System.out.println(strRet);
            }
        }

    }

    @Override
    public void sync(int year, int month, String deptCode) {
        logger.debug("客户基础数据同步开始：1/10");
        syncBaseData(BusinessRelationTypeEnum.CUSTOMER);

        logger.debug("供应商基础数据同步开始：2/10");
        syncBaseData(BusinessRelationTypeEnum.SUPPLIER);

        //采购入库
        logger.debug("采购入库同步开始：3/10");
        syncPurchase(year, month, deptCode);

        //生产领料
        logger.debug("生产领料同步开始：4/10");
        syncProductionBom(year, month, deptCode);

        //其它领料
        logger.debug("其它领料同步开始：5/10");
        syncProductionOther(year, month, deptCode);

        //生产入库
        logger.debug("生产入库同步开始：6/10");
        syncProductionIn(year, month, deptCode);

        //销售出库
        logger.debug("销售出库同步开始：7/10");
        syncSaleOut(year, month, deptCode);

        //质保出库
        logger.debug("质保出库同步开始：8/10");
        syncSaleService(year, month, deptCode);

        //销售发票
        logger.debug("销售发票同步开始：9/10");
        syncSaleInvoice(year, month, deptCode);

        //采购发票
        logger.debug("采购发票同步开始：10/10");
        syncPurchaseInvoice(year, month, deptCode);

        logger.debug("同步完成！！！");
    }

    @Override
    public void sync(String accountId) {
        AccountMonth month = inventoryService.queryAccountMonthById(accountId);
        if (month == null) {
            throw new BusinessEntityNotFoundException(accountId, "会计期间");
        }
        String deptCode = configureService.getValueByCode(InventoryConfigureCode.tenant_finance_dept_code, month.getTenantId());
        if (StringUtils.isNullOrEmpty(deptCode)) {
            throw new BusinessException(String.format("当前租户【%s】没有配置对应的核算部门代码", SecurityUtils.getUserContent().getTenantName()));
        }

        sync(month.getAccountYear(), month.getAccountMonth(), deptCode);
    }

    @Override
    public void syncComplete(String accountId) {
        inventoryService.syncFinanceComplete(accountId);
    }

    @Override
    public void syncPurchase(int year, int month, String deptCode) {
        for (PurchaseMonthSumExt item : purchaseOrderService.queryMonthSum(year, month)) {

            if (item.getPurchaseMoney() == 0) {
                continue;
            }

            double money = new BigDecimal(item.getPurchaseMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue();
            CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);
            m.getModel().addDetail("1403", String.format("%s采购暂估", item.getEntName()), money, AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
            m.getModel().addDetail("2202.01", String.format("%s采购暂估", item.getEntName()), money, AccountTypeEnum.credit, FDetailDto.valueSupplier(item.getEntCode().toString()));

            syncCertificate(m);
        }
    }

    @Override
    public void syncProductionIn(int year, int month, String deptCode) {
        Double money = inventoryNoteService.queryCostMoney(InventoryTypeEnum.PRODUCTION_IN, year, month);
        CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);
        m.getModel().addDetail("1405", String.format("本月生产入库"), money, AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        m.getModel().addDetail("5001.01", String.format("本月生产入库"), money, AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));

        syncCertificate(m);
    }

    @Override
    public void syncProductionBom(int year, int month, String deptCode) {
        double money = new BigDecimal(inventoryNoteService.queryCostMoney(InventoryTypeEnum.PRODUCTION_BOM_USER, year, month))
                .add(new BigDecimal(inventoryNoteService.queryCostMoney(InventoryTypeEnum.REPAIR_USER, year, month))).doubleValue();

        CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);
        m.getModel().addDetail("5001.01", String.format("本月直接材料领用"), money, AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        m.getModel().addDetail("1403", String.format("本月直接材料领用"), money, AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));

        syncCertificate(m);
    }

    @Override
    public void syncProductionOther(int year, int month, String deptCode) {
        double money = inventoryNoteService.queryCostMoney(InventoryTypeEnum.PRODUCTION_USER, year, month);
        if (money == 0) {
            return;
        }

        CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);
        m.getModel().addDetail("5001.03.14", String.format("本月消耗品领用"), money, AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        m.getModel().addDetail("1403", String.format("本月消耗品领用"), money, AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));

        syncCertificate(m);
    }

    @Override
    public void syncSaleOut(int year, int month, String deptCode) {
        FinanceBySaleExt note = saleOrderService.queryFinanceBySale(year, month);
        if (note == null) {
            return;
        }
        CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);

        m.getModel().addDetail("1406", String.format("本月发出商品"), new BigDecimal(note.getCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        m.getModel().addDetail("1405", String.format("本月发出商品"), new BigDecimal(note.getProductionCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));
        m.getModel().addDetail("1403", String.format("本月发出商品"), new BigDecimal(note.getPurchaseCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));

        syncCertificate(m);
    }

    @Override
    public void syncSaleService(int year, int month, String deptCode) {
        FinanceBySaleExt note = saleOrderService.queryFinanceByService(year, month);
        if (note == null) {
            return;
        }

        CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(year, month, 20), false);
        m.getModel().addDetail("6601.13.01", String.format("本月质保材料出库"), new BigDecimal(note.getCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        if (note.getProductionCostMoney() != 0) {
            m.getModel().addDetail("1405", String.format("本月质保材料出库"), new BigDecimal(note.getProductionCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));
        }
        if (note.getPurchaseCostMoney() != 0) {
            m.getModel().addDetail("1403", String.format("本月质保材料出库"), new BigDecimal(note.getPurchaseCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));
        }

        syncCertificate(m);
    }

    @Override
    public void syncSaleInvoice(int year, int month, String deptCode) {
        BigDecimal sumCostMoney = new BigDecimal(0).setScale(2, RoundingMode.HALF_UP);

        List<InvoiceNoteExt> list = invoiceNoteService.queryNotSendFinance(InvoiceTypeEnum.Sale, year, month);
        if (list.size() == 0) {
            return;
        }
        for (InvoiceNoteExt note : list) {

            if (note.getInvoiceMoney().intValue() == 0) {
                continue;
            }

            //不含税金额
            double notRateMoney = new BigDecimal(note.getInvoiceMoneyNotTax()).setScale(2, RoundingMode.HALF_UP).doubleValue();
            //税金额
            double rateMoney = new BigDecimal(note.getInvoiceMoney()).subtract(new BigDecimal(notRateMoney)).setScale(2, RoundingMode.HALF_UP).doubleValue();
            double invoiceMoney = new BigDecimal(note.getInvoiceMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue();

            CertificateModel m = CertificateModel.create("001", 0, DateUtils.parseDate(note.getSubmitTime(), "yyyy-MM-dd"), false);
            //应收账款
            m.getModel().addDetail("1122", String.format("应收%s货款", note.getEntName()), invoiceMoney, AccountTypeEnum.debit, FDetailDto.valueCustomer(note.getEntCode().toString()));
            //主营业务收入
            m.getModel().addDetail("6001.01", String.format("%s销售收入", note.getEntName()), notRateMoney, AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));
            //应交税费
            m.getModel().addDetail("2221.01.02", String.format("%s销项税额", note.getInvoiceCode()), rateMoney, AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));

            sumCostMoney = sumCostMoney.add(new BigDecimal(note.getCostMoney().intValue() == 0 ? 0.01d : new BigDecimal(note.getCostMoney()).setScale(2, RoundingMode.HALF_UP).doubleValue()));

            syncCertificate(m);
        }

        CertificateModel mCost = CertificateModel.create("001", 0, DateUtils.getLastDayOfMonth(year, month), false);
        //主营业务成本
        mCost.getModel().addDetail("6401.01", String.format("结转本月销售成本"), sumCostMoney.intValue() == 0 ? 0.01d : sumCostMoney.setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
        //发出商品
        mCost.getModel().addDetail("1406", String.format("结转本月销售成本"), sumCostMoney.intValue() == 0 ? 0.01d : sumCostMoney.setScale(2, RoundingMode.HALF_UP).doubleValue(), AccountTypeEnum.credit, FDetailDto.valueDept(deptCode));
        syncCertificate(mCost);
    }

    @Override
    public void syncPurchaseInvoice(int year, int month, String deptCode) {
        List<InvoiceNoteByPurchaseSum> list = invoiceNoteService.queryPurchaseSum(year, month);
        if (list.size() == 0) {
            return;
        }

        for (InvoiceNoteByPurchaseSum note : list) {
            if (note.getInvoiceMoney().intValue() == 0) {
                continue;
            }

            Date dt = DateUtils.getLastDayOfMonth(year, month);
            //不含税金额
            //double notRateMoney = new BigDecimal(note.getInvoiceMoney()).divide(new BigDecimal(1 + note.getTaxRate()), 2, RoundingMode.HALF_UP).doubleValue();
            double notRateMoney = new BigDecimal(note.getCostMoney()).doubleValue();
            //税金额
            double rateMoney = new BigDecimal(note.getInvoiceMoney()).subtract(new BigDecimal(notRateMoney)).doubleValue();

            CertificateModel m = CertificateModel.create("001", 0, dt, false);
            //暂估应付款
            m.getModel().addDetail("2202.01", String.format("%s采购入库", note.getEntName()), note.getCostMoney(), AccountTypeEnum.debit, FDetailDto.valueSupplier(note.getEntCode().toString()));
            //进项税
            m.getModel().addDetail("2221.01.03", String.format("%s采购入库", note.getEntName()), rateMoney, AccountTypeEnum.debit, FDetailDto.valueDept(deptCode));
            //价税合计
            m.getModel().addDetail("2202.02", String.format("%s采购入库", note.getEntName()), note.getInvoiceMoney(), AccountTypeEnum.credit, FDetailDto.valueSupplier(note.getEntCode().toString()));

            syncCertificate(m);
        }
    }

    private K3Result syncCertificate(CertificateModel model) {
        String ret = null;
        if (this.soap == null) {
            WebService1 ws = new WebService1();
            soap = ws.getWebService1Soap();
        }

        try {
            ret = soap.certificate("0", JSONObject.toJSONString(model), k3Config.getUserName(), k3Config.getPassword());
            if (JSONObject.parseObject(ret).containsKey("code")) {
                String code = JSONObject.parseObject(ret).get("code").toString();
                if ("1".equals(code)) {
                    //成功
                    logger.debug(String.format("id : %s , %s", JSONObject.parseObject(ret).get("id").toString(), model.getModel().getEntity().get(0).getExplanation()));
                    return K3Result.getInstance(JSONObject.parseObject(ret).get("id").toString(), JSONObject.parseObject(ret).get("Number").toString());
                } else {
                    //失败
                    logger.error("同步失败：" + ret);
                    logger.error("同步失败：" + JSONObject.toJSONString(model));
                }
            } else {
                logger.error("同步失败：" + ret);
                logger.error("同步失败：" + JSONObject.toJSONString(model));
            }
        } catch (Exception e) {
            if (ret != null) {
                logger.error("同步失败：" + ret);
            }
            logger.error("同步失败：" + e.getMessage());
        }
        return null;
    }
}
